using System.Collections.Concurrent;
using SaToken.SaToken.Application;

namespace SaToken.SaToken.Session;

/// <summary>
///     Session 模型，会话作用域的读取值对象
/// </summary>
[Serializable]
public class SaSession : ISaSetValueInterface
{
    /// <summary>
    ///     在 SaSession 上存储用户对象时建议使用的 key
    /// </summary>
    public const string User = "USER";

    /// <summary>
    ///     在 SaSession 上存储角色列表时建议使用的 key
    /// </summary>
    public const string RoleList = "ROLE_LIST";

    /// <summary>
    ///     在 SaSession 上存储权限列表时建议使用的 key
    /// </summary>
    public const string PermissionList = "PERMISSION_LIST";

    /// <summary>
    ///     所有挂载数据
    /// </summary>
    private ConcurrentDictionary<string, object> _dataMap = new();


    public SaSession()
    {
    }

    /// <summary>
    ///     构建一个 Session 对象
    /// </summary>
    /// <param name="id">Session的id</param>
    public SaSession(string id)
    {
        Id = id;
        CreateTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
    }

    /// <summary>
    ///     此 SaSession 的 id
    /// </summary>
    public string? Id { get; set; }

    /// <summary>
    ///     此 SaSession 的类型
    /// </summary>
    public string? Type { get; set; }

    /// <summary>
    ///     所属 loginType
    /// </summary>
    public string? LoginType { get; set; }

    /// <summary>
    ///     所属 loginId（当此 SaSession 属于 Account-Session 时，此值有效）
    /// </summary>
    public object? LoginId { get; set; }

    /// <summary>
    ///     此 SaSession 的创建时间（13位时间戳）
    /// </summary>
    public long CreateTime { get; set; }

    /// <summary>
    ///     获取数据映射（修改后需调用Update()避免脏数据）
    /// </summary>
    public ConcurrentDictionary<string, object> DataMap => _dataMap;

    /// <summary>
    ///     删除指定键
    /// </summary>
    /// <param name="key">键</param>
    /// <returns>当前对象</returns>
    public ISaSetValueInterface Delete(string key)
    {
        _dataMap.TryRemove(key, out _);
        Update();
        return this;
    }


    // ----------------------- 角色权限管理

    /// <summary>
    ///     获取用户角色列表
    /// </summary>
    /// <returns>角色列表</returns>
    public List<string> GetRoleList()
    {
        var roles = Get(RoleList);
        return roles as List<string> ?? new List<string>();
    }

    /// <summary>
    ///     设置用户角色列表
    /// </summary>
    /// <param name="roleList">角色列表</param>
    public void SetRoleList(List<string> roleList)
    {
        Set(RoleList, roleList);
    }

    /// <summary>
    ///     获取用户权限列表
    /// </summary>
    /// <returns>权限列表</returns>
    public List<string> GetPermissionList()
    {
        var permissions = Get(PermissionList);
        return permissions as List<string> ?? new List<string>();
    }

    /// <summary>
    ///     设置用户权限列表
    /// </summary>
    /// <param name="permissionList">权限列表</param>
    public void SetPermissionList(List<string> permissionList)
    {
        Set(PermissionList, permissionList);
    }

    /// <summary>
    ///     检查是否具有指定角色
    /// </summary>
    /// <param name="role">角色</param>
    /// <returns>是否具有该角色</returns>
    public bool HasRole(string role)
    {
        return GetRoleList().Contains(role);
    }

    /// <summary>
    ///     检查是否具有指定权限
    /// </summary>
    /// <param name="permission">权限</param>
    /// <returns>是否具有该权限</returns>
    public bool HasPermission(string permission)
    {
        return GetPermissionList().Contains(permission);
    }


    // ----------------------- 会话操作

    /// <summary>
    ///     更新Session（持久化刷新）
    /// </summary>
    public void Update()
    {
        SaManager.GetSaTokenDao().UpdateSession(this);
    }

    /// <summary>
    ///     注销Session（从持久库删除）
    /// </summary>
    public void Logout()
    {
        SaManager.GetSaTokenDao().DeleteSession(Id);
    }

    /// <summary>
    ///     获取Session剩余存活时间（秒）
    /// </summary>
    /// <returns>剩余存活时间</returns>
    public long Timeout()
    {
        return SaManager.GetSaTokenDao().GetSessionTimeout(Id);
    }

    /// <summary>
    ///     修改Session剩余存活时间
    /// </summary>
    /// <param name="timeout">过期时间（秒）</param>
    public void UpdateTimeout(long timeout)
    {
        SaManager.GetSaTokenDao().UpdateSessionTimeout(Id, timeout);
    }

    /// <summary>
    ///     修改Session最小剩余存活时间（仅当当前过期时间小于指定值时生效）
    /// </summary>
    /// <param name="minTimeout">最小过期时间（秒）</param>
    public void UpdateMinTimeout(long minTimeout)
    {
        var min = Trans(minTimeout);
        var curr = Trans(Timeout());
        if (curr < min) UpdateTimeout(minTimeout);
    }

    /// <summary>
    ///     修改Session最大剩余存活时间（仅当当前过期时间大于指定值时生效）
    /// </summary>
    /// <param name="maxTimeout">最大过期时间（秒）</param>
    public void UpdateMaxTimeout(long maxTimeout)
    {
        var max = Trans(maxTimeout);
        var curr = Trans(Timeout());
        if (curr > max) UpdateTimeout(maxTimeout);
    }

    /// <summary>
    ///     转换过期时间（-1表示永不过期）
    /// </summary>
    private static long Trans(long value)
    {
        return value == SaTokenDao.NEVER_EXPIRE ? long.MaxValue : value;
    }


    // ----------------------- 数据挂载操作

    /// <summary>
    ///     获取指定key的值
    /// </summary>
    /// <param name="key">键</param>
    /// <returns>值</returns>
    public object? Get(string key)
    {
        _dataMap.TryGetValue(key, out var value);
        return value;
    }

    /// <summary>
    ///     设置键值对
    /// </summary>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <returns>当前对象</returns>
    public SaSession Set(string key, object value)
    {
        _dataMap[key] = value;
        Update();
        return this;
    }

    /// <summary>
    ///     仅当键不存在时设置值
    /// </summary>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <returns>当前对象</returns>
    public SaSession SetByNull(string key, object value)
    {
        if (!_dataMap.TryAdd(key, value)) return this;
        Update();
        return this;
    }

    /// <summary>
    ///     获取所有键
    /// </summary>
    /// <returns>键集合</returns>
    public ICollection<string> Keys()
    {
        return _dataMap.Keys;
    }

    /// <summary>
    ///     清空所有数据
    /// </summary>
    public void Clear()
    {
        _dataMap.Clear();
        Update();
    }
}